package cn.edu.tjufe.llp.tin.service.sys;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.hibernate.Hibernate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.edu.tjufe.llp.tin.common.PasswordMismatchException;
import cn.edu.tjufe.llp.tin.common.RecordNotFoundException;
import cn.edu.tjufe.llp.tin.dto.sys.SysUserDto;
import cn.edu.tjufe.llp.tin.dto.sys.SysUserMapper;
import cn.edu.tjufe.llp.tin.enums.user.AccountEnabled;
import cn.edu.tjufe.llp.tin.model.sys.SysRole;
import cn.edu.tjufe.llp.tin.model.sys.SysUser;
import cn.edu.tjufe.llp.tin.repository.sys.SysRoleRepository;
import cn.edu.tjufe.llp.tin.repository.sys.SysUserRepository;


@Service
@Transactional
public class SysUserServiceImpl implements SysUserService{
	@Autowired
	private SysUserRepository sysUserRepository;

	@Autowired
	private SysRoleRepository sysRoleRepository;
	
	@Autowired
	private PasswordEncoder passwordEncoder;
	
	@Override
	@Transactional(readOnly = true)
	public SysUser findByUsername(String username) {
		SysUser entity = sysUserRepository.findByUsername(username)
									.orElseThrow(() -> new RecordNotFoundException());
		Hibernate.initialize(entity.getRoles());
		return entity;
	}
	
	@Override
	@Transactional(readOnly = true)
	public boolean existsByUsername(String username) {
		return sysUserRepository.existsByUsername(username);
	}

	@Override
	@Transactional(readOnly = true)
	public SysUser findById(long id) {
		SysUser entity = sysUserRepository.findById(id)
									.orElseThrow(() -> new RecordNotFoundException());
		Hibernate.initialize(entity.getRoles());
		return entity;
	}

	@Override
	@Transactional(readOnly = true)
	public List<SysUser> findLike(String name) {
		return sysUserRepository.findByUsernameContains(name);
	}

	@Override
	@Transactional(readOnly = true)
	public List<SysUser> findAll() {
		List<SysUser> entities = sysUserRepository.findAll();
		
		return entities;
	}

	@Override
	public SysUser create(SysUser pojo) {
		pojo.setPassword(passwordEncoder.encode(pojo.getPassword()));
		return sysUserRepository.save(pojo);
	}

	@Override
	public long signUp(SysUserDto userDto) {
		
		SysUser entity = SysUserMapper.INSTANCE.toEntityWithRoles(userDto);
		entity.setPassword(passwordEncoder.encode(userDto.getPassword()));
		
		entity = sysUserRepository.save(entity);
		
		return entity.getId();
	}
	
	
	@Override
	public SysUser update(SysUser pojo) {
		SysUser entity = sysUserRepository.findById(pojo.getId())
									.orElseThrow(() -> new RecordNotFoundException());

		Set<Long> oldRoleIds = entity.getRoles().stream().map(SysRole::getId).collect(Collectors.toSet());
		
		// sysUserRepository.evict(entity);
		
		Set<Long> newRoleIds = pojo.getRoles().stream().map(SysRole::getId).collect(Collectors.toSet());;
		
		// 计算哪些角色需要从数据库里移除（原来有，现在没有）
		Set<Long> toBeRemoved = new HashSet<>(oldRoleIds);
		toBeRemoved.removeAll(newRoleIds);
		
		// 计算需要将哪些角色加到数据库里（现在有，原来没有）
		Set<Long> toBeAdded = new HashSet<>(newRoleIds);
		toBeAdded.removeAll(oldRoleIds);
		
		// 从实体中，去掉已经取消的角色
		entity.getRoles().removeIf(e -> toBeRemoved.contains(e.getId()));
		
		// 过滤pojo，找到最终需要添加的角色
		// Set<SysRole> newRoles = new HashSet<>(pojo.getRoles());
		// newRoles.removeIf(e -> !toBeAdded.contains(e.getId()));
		
		Set<SysRole> newRoles = new HashSet<>();
		toBeAdded.forEach(id -> {
			sysRoleRepository.findById(id).ifPresent((role) -> newRoles.add(role));
		});
		
		entity.getRoles().addAll(newRoles);
		
		BeanUtils.copyProperties(pojo, entity, "deleted", "roles", "password");
		
		return sysUserRepository.save(entity);
	}

	@Override
	public SysUser changPassword(long id, String newPassword) {
		SysUser entity = sysUserRepository.findById(id)
									.orElseThrow(() -> new RecordNotFoundException());
		// sysUserRepository.evict(entity);

		entity.setPassword(passwordEncoder.encode(newPassword));

		return sysUserRepository.save(entity);
	}

	

	@Override
	public void deleteById(long id) {
		if(!sysUserRepository.existsById(id)) {
			throw new RecordNotFoundException();
		}
		sysUserRepository.deleteById(id);
	}
	
	@Override
	public void disableById(long id) {
		SysUser entity = sysUserRepository.findById(id)
									.orElseThrow(() -> new RecordNotFoundException());

		entity.setEnabled(AccountEnabled.F);
		
		// sysUserRepository.save(entity);
	}
	
	//=========================================================================

	@Override
	public SysUser updateSelfInfo(SysUser pojo) {
		SysUser entity = sysUserRepository.findById(pojo.getId())
									.orElseThrow(() -> new RecordNotFoundException());

		// 演示一下不采用乐观锁的方式，不用调用dao里的update函数，数据会自动保存到数据库，代码要简洁一些。
		BeanUtils.copyProperties(pojo, entity, "deleted", "roles", "password", "status");

		return entity;
	}

	@Override
	public SysUser changSelfPassword(long id, String currentPassword, String newPassword) {
		SysUser entity = sysUserRepository.findById(id)
									.orElseThrow(() -> new RecordNotFoundException());
		if(!passwordEncoder.matches(currentPassword, entity.getPassword())) {
			throw new PasswordMismatchException("当前密码错误。");
		}
		
		// 演示一下不采用乐观锁的方式，不用调用dao里的update函数，数据会自动保存到数据库，代码要简洁一些。
		entity.setPassword(passwordEncoder.encode(newPassword));

		return entity;
	}

}
